/*
作者：hexiaojun
*/
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiAP.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>
#include <TFT_eSPI.h> 
#include <SPI.h>
#include <Wire.h>
#include <INA226.h>
#include <PubSubClient.h>
#include "EEPROM.h"
#include "myFonts.h"
#include "EC11_Driver.h"
//blinker
#define BLINKER_WIFI
#include <Blinker.h>

//系统参数
#define ESP_PIN_BACKFLOW_MOS                           27     //mos管下管控制
#define ESP_PIN_PWM                                    33     //PWM输出引脚
#define ESP_PIN_PWM_EN                                 32     //MOS管驱动使能
#define ESP_PIN_FAN                                    16     //风扇控制
#define ESP_PIN_LED                                     2     //LED指示灯
#define ESP_PIN_OUTPUT                                 17     //输出控制
#define ESP_PIN_EC11_A                                 18     //输出控制
#define ESP_PIN_EC11_B                                 19     //输出控制
#define ESP_PIN_EC11_K                                 23     //输出控制
#define ESP_PIN_IIC_SDA                                21     //I2C SDA
#define ESP_PIN_IIC_SCL                                22     //I2C SCL
#define ESP_PIN_NTC                                    39     //NTC电阻，下拉电阻10k
#define ESP_PIN_INA_ALERT_1                            35     //INA226 Alert
#define ESP_PIN_INA_ALERT_2                            34     //INA226 Alert
#define ESP_PIN_INA_ALERT_3                             4     //INA226 Alert
#define ESP_PIN_SCREEN_BL                              12     //屏幕背光控制
#define EEPROM_SIZE                                  4095     //eeprom大小
#define PCBA_CURRENT_LIMIT                             40     //电路板最大电流限制
#define PCBA_VOLTAGE_LIMIT                             90     //电路板最大电压限制
#define NTP1                                           "ntp.aliyun.com"
#define NTP2                                           "ntp1.aliyun.com"
#define NTP3                                           "ntp.ntsc.ac.cn"

//系统运行参数，按实际使用情况修正
const String PROGMEM sys_version           = "v23.12.271";    //固件版本
TaskHandle_t Core2;                                           //启用esp32的第二个核心
bool system_PCBA_mode                                =  1;    //电路板工作模式，0-DCDC模式，1-mppt模式
uint8_t system_restore_factory_settings              =  5;    //恢复出厂设置确认次数 
uint16_t system_mqtt_keepalive                       = 30;    //MQTT keepAlive时间，单位s
int system_pwm_channel                               =  0;    //PWM 通道
int system_pwm_frequency                          = 100000;    //PWM 开关频率 单位 Hz  39000
int system_pwm_resolution                            = 8;    //PWM 位分辨率     39-11      100-8
int mqtt_server_port                               =    0;    //MQTT服务端口号
int E_add_start                                    =    0;    //EEPROM存储开始地址
float system_pwm_maxDC                             = 95.0;    //最大工作占空比 (单位%) 一般为90%-97%
float system_ppwm_margin                           = 99.5;    //预测 PWM 校正因子 (%)
float system_voltage_min                           =  9.0;    //系统工作最低电压
float system_bat_current_margin                    =  1.1;    //电池充电过流触发限制，超过设置参数乘该值立即关闭
float system_bat_voltage_margin                    = 1.05;    //电池充电电压触发限制，超过设置参数乘该值立即关闭
float system_ina_Max_excepted_current_1              = 40;    //最大采样电流
float system_ina_Max_excepted_current_2              = 40;    //最大采样电流
float system_ina_Max_excepted_current_3              = 40;    //最大采样电流
float system_ina_voltage_correct_1                 =  0.0;    //ina226 0x40 修正电压因子
float system_ina_voltage_correct_2                 =  0.0;    //ina226 0x41 修正电压因子
float system_ina_voltage_correct_3                 =  0.0;    //ina226 0x42 修正电压因子
float system_ina_current_correct_1                 =  0.0;    //ina226 0x40 修正电流因子
float system_ina_current_correct_2                 =  0.0;    //ina226 0x41 修正电流因子
float system_ina_current_correct_3                 =  0.0;    //ina226 0x42 修正电流因子
float system_voltage_dropout                       =  1.0;    //降压电路压降，由于最大占空比限制造成的压降
float system_light_control_openv                   =  9.0;    //光控模式输出开启电压
unsigned long mqtt_refresh_state_delay             = 5000;    //mqtt刷新状态数据间隔，心跳间隔
unsigned long system_storedata_timeinterval    = 10800000;    //历史数据保存时间间隔，单位ms
unsigned long system_batcur_clocker                =    0;    //电池过流消抖计时器
unsigned long system_batcur_time                   =  100;    //电池过流消抖时间，单位ms
unsigned long system_batvol_clocker                =    0;    //电池过压消抖计时器
unsigned long system_batvol_time                   =  100;    //电池过压消抖时间，单位ms

//NTC 10K阻值列表，分三段加快查表速度
const float PROGMEM ntc_10k_l20_value[] = {97120, 91660, 86540, 81720, 77220, 72980, 69000, 65260, 61760, 58460, 55340, 52420, 49660, 47080, 44640, 42340, 40160, 38120, 36200, 34380};
const float PROGMEM ntc_10k_u61_value[] = {32660, 31040, 29500, 28060, 26680, 25400, 24180, 23020, 21920, 20880, 19900, 18970, 18090, 17260, 16460, 15710, 15000, 14320, 13680, 13070, 12490, 
                                  11940, 11420, 10920, 10450, 10000, 9574, 9166, 8778, 8408, 8058, 7722, 7404, 7098, 6808, 6532, 6268, 6016, 5776, 5546, 5326, 5118, 4918, 4726, 4544, 
                                  4368, 4202, 4042, 3888, 3742, 3602, 3468, 3340, 3216, 3098, 2986, 2878, 2774, 2674, 2580, 2488};
const float PROGMEM ntc_10k_h60_value[] = {2400, 2316, 2234, 2158, 2082, 2012, 1942, 1876, 1813, 1751, 1693, 1637, 1582, 1530, 1480, 1432, 1385, 1341, 1298, 1256, 1216, 1178, 1141, 1105, 1071, 
                                  1038, 1006, 975, 945.2, 916.4, 888.8, 862, 836.4, 811.4, 787.4, 764.2, 741.8, 720.2, 699.4, 679.2, 659.6, 640.8, 622.6, 605, 588, 571.4, 555.6, 540.2, 
                                  525.2, 510.8, 496.8, 483.2, 470.2, 457.4, 445.2, 433.4, 421.8, 410.6, 399.8, 389.4};
//系统运行参数, 默认值在eeprom中
float output_current_limit                         =    0;    //输出电流限制
float system_bat_maxv                              =    0;    //电池充电电压
float system_bat_fullv                             =    0;    //电池满电电压
float system_bat_minv                              =    0;    //电池最小电压
float system_bat_protectv                          =    0;    //电池保护电压
float system_bat_current_limit                     =    0;    //电池充电及输出电流限制
float system_ina_resistor_1                        =    0;    //电流采样电阻
float system_ina_resistor_2                        =    0;    //电流采样电阻
float system_ina_resistor_3                        =    0;    //电流采样电阻
uint8_t system_internet_mode                       =    0;    //0-离线模式，1-MQTT模式，2-Blinker模式
uint8_t system_output_control_mode                 =    0;    //0-普通模式，1-光控模式，2-光控+时控
uint8_t system_output_optical_control_time         =    0;    //光控+时控时打开时间，单位：小时
uint8_t system_lcd_backlight_time                  =    0;    //0-常开，1-3分钟，2-5分钟，3-10分钟，4-15分钟，5-30分钟
uint8_t system_fan_fanOpen_TEM                     =    0;    //风扇启动温度
uint8_t system_protect_TEM                         =    0;    //系统保护温度
uint8_t system_enable_OTA                          =    0;    //OTA开关，0-关，1-开
uint8_t system_output_state                        =    0;    //输出控制，0-开，1-关
uint8_t system_uart_print_state                    =    0;    //串口输出状态开关，0-关，1-开
String wifi_ssid                                   =   "";    //wifi名称
String wifi_psd                                    =   "";    //wifi密码
String blinker_auth                                =   "";    //Blinker密钥
String mqtt_server_add                             =   "";    //MQTT服务器地址
String mqtt_usr                                    =   "";    //MQTT登录用户名
String mqtt_psd                                    =   "";    //MQTT登录密码
String system_ota_usr                              =   "";    //OTA升级用户名
String system_ota_psd                              =   "";    //OTA升级密钥

//系统运行参数
bool system_buck_enable                            =    0;    //降压模式是否启用
bool system_charging_pause                         =    0;    //充电暂停，0-正常，1-暂停
bool system_mppt_mode                              =    1;    //0-使用cc-cv算法，1-使用mppt算法
bool system_REC                                    =    0;    //发现太阳能输入
bool system_set_internet                           =    0;    //配网模式，0-关，1-开
bool system_set_initialized                        =    0;    //配网模式初始化，0-未初始化，1-已初始化
bool system_ota_initialized                        =    0;    //OTA初始化状态，0-未初始化，1-已初始化
bool system_mqtt_initialized                       =    0;    //MQTT初始化状态，0-未初始化，1-已初始化
int system_error                                   =    0;    //系统错误数量
int system_pwm_value                               =    0;    //pwm占空比，开机默认0
int system_ppwm_value                              =    0;    //预测pwm占空比
int system_pwm_max                                 =    0;    //pwm最大值
int system_pwm_max_limit                           =    0;    //PWM最大限制
int system_temperature_ntc                         =    0;    //温度，来自10k ntc
int system_battery_percent                         =    0;    //电池电量百分比
int system_mppt_efficiency                         =    0;    //mppt效率 %
int system_daily_work_over_date                    =    0;    //每日工作完成日期，等于今天表示已完成，等于昨天今天未完成
float system_ina_voltage_1                         =  0.0;    //ina226 0x40 读取电压
float system_ina_voltage_2                         =  0.0;    //ina226 0x41 读取电压
float system_ina_voltage_3                         =  0.0;    //ina226 0x42 读取电压
float system_ina_current_1                         =  0.0;    //ina226 0x40 读取电流
float system_ina_current_2                         =  0.0;    //ina226 0x41 读取电流
float system_ina_current_3                         =  0.0;    //ina226 0x42 读取电流
float system_ina_power_1                           =  0.0;    //ina226 0x40 功率
float system_ina_power_2                           =  0.0;    //ina226 0x41 功率
float system_ina_power_3                           =  0.0;    //ina226 0x42 功率
float system_ina_voltage_1_prev                    =  0.0;    //mppt计算，存储先前的电压
float system_ina_power_1_prev                      =  0.0;    //mppt计算，存储先前的功率
float system_power_meter_in                        =  0.0;    //日输入电量累计，单位wh
float system_power_meter_bat                       =  0.0;    //日电池电量累计，单位wh
float system_power_meter_out                       =  0.0;    //日输出电量累计，单位wh
float store_in_kwh                                 =  0.0;    //累计发电量
float store_bat_kwh                                =  0.0;    //累计电池电量（充电+用电）
float store_out_kwh                                =  0.0;    //累计输出电量
String sys_ota_add_suffix                          =   "";    //用于加密网址
String sys_mac_address                                ="";    //mac地址
String sys_mac_address_pure                           ="";    //mac地址无冒号
String sys_ip_address                                 ="";    //ip地址
String wifi_host                                   =   "";    //OTA host
String settings_ap_ssid                            =   "";    //配网时AP名称
String settings_ap_psd                             =   "";    //配网时连接密码
//系统运行计时类
unsigned long system_storedata_lastmillis             = 0;    //记录上一次自动保存数据到eeprom时间
unsigned long system_millis_last_internet      = millis();    //networkController 1s计时用
unsigned long mqtt_reconncet_lastmillis               = 0;    //mqtt重新连接计时
unsigned long mqtt_sendmsg_lastmillis                 = 0;    //mqtt信息发送频率计时
unsigned long mqtt_reconnect_lastmillis               = 0;    //wifi连接失败，重新连接计时
unsigned long esp_ouput_open_millis                   = 0;    //用于输出打开计时，单位ms
unsigned long system_millis_last_sensors        = millis();   //计时器用于记录上次传感器读取时间
unsigned long system_millis_last_meter          = millis();   //用于电量统计，记录上次计算电量时间
unsigned long system_millis_last_screen_1       = millis();   //屏幕刷新计时，1s
unsigned long system_millis_last_screen_5       = millis();   //屏幕刷新计时，5s
unsigned long system_millis_backlight                 = 0;    //用于背光点亮计时
unsigned long ota_resetup_millis                      = 0;    //ota用于初化失败后重新初始化计时
//用于屏幕显示
uint8_t screen_page_now                               = 0;    //0主界面，100-199为设置页面，200-254备用
uint8_t screen_page_100_selected                      = 0;    //用于记录屏幕当前选择行
uint8_t sys_RFS_count                                 = 0;    //恢复出厂设置点击次数
const uint8_t Knob_CW                                 = 1;    //EC11顺时针
const uint8_t Knob_CCW                                = 0;    //EC11逆时针

/*系统报警信息
16位，未使用部分置1，从右向左定义：
9=高温                  0xFF00   0xFEFF
8=输入过流              0xFE80   0xFF7F
7=输出过流              0xFE40   0xFFBF
6=过压                  0xFE20   0xFFDF
5=欠压                  0xFE10   0xFFEF
4=电池输出保护           0xFE08   0xFFF7
3=输入电压低于电池电压   0xFE04   0xFFFB 
2=电池电压低于设定值     0xFE02   0xFFFD
1=风扇状态              0xFE01   0xFFFE
*/
unsigned short system_info_marker                = 0xFE00;     // 1111 1110 0000 0000
unsigned short system_info_marker_before         = 0xFE00;     // 1111 1110 0000 0000

//系统时间
struct tm local_time;                                         //系统时间
//MQTT
uint8_t MQTT_WILL_QOS             =            0;                                      //MQTT QOS
//MQTT主题前辍
String topic_will                 =            "homeassistant/mppt/";                  //遗嘱
String topic_read_pvc             =            "homeassistant/mppt/";                  //读取电流、电压、功率
String topic_read_state           =            "homeassistant/mppt/";                  //温度读取、累计电量
String topic_command_output       =            "homeassistant/mppt/";                  //输出开关
String topic_command_opentime     =            "homeassistant/mppt/";                  //设置光控+时控模式打开时间
String topic_command_outtype      =            "homeassistant/mppt/";                  //输出模式
String topic_log                  =            "homeassistant/mppt/";                  //日志
String topic_command_settings     =            "homeassistant/mppt/";                  //远程设置
//BLINKER
BlinkerNumber num_pvp("num-pvp");                                  //光伏输入功率
BlinkerNumber num_pvv("num-pvv");                                  //光伏输入电压
BlinkerNumber num_pvc("num-pvc");                                  //光伏输入电流
BlinkerNumber num_batp("num-batp");                                //电池功率
BlinkerNumber num_batv("num-batv");                                //电池电压
BlinkerNumber num_batc("num-batc");                                //电池电流
BlinkerNumber num_op("num-op");                                    //输出功率
BlinkerNumber num_ov("num-ov");                                    //输出电压
BlinkerNumber num_oc("num-oc");                                    //输出电流
BlinkerNumber num_tem("num-tem");                                  //温度
BlinkerNumber num_inwh("num-inwh");                                //输入电量
BlinkerNumber num_batwh("num-batwh");                              //电池电量
BlinkerNumber num_outwh("num-outwh");                              //输出电量
BlinkerNumber num_batpp("num-batpp");                              //电池电量百分比
BlinkerNumber num_mpptp("num-mpptp");                              //MPPT效率百分比
BlinkerNumber num_totalin("num-totalin");                          //累计输入电量
BlinkerNumber num_totalbat("num-totalbat");                        //累计电池电量
BlinkerNumber num_totalout("num-totalout");                        //累计输出电量
BlinkerButton btn_otcr("btn-otcr");                                //输出开关
BlinkerButton btn_otmd("btn-otmd");                                //输出模式切换
BlinkerButton btn_ottm("btn-ottm");                                //光控+时控模式打开时间
BlinkerText blinker_msg("tex-msg");                                //文本输出

//初始化硬件
TFT_eSPI SCREEN = TFT_eSPI();
EC11 ec11(ESP_PIN_EC11_A, ESP_PIN_EC11_B, ESP_PIN_EC11_K, 4);
INA226 ina_40;
INA226 ina_41;
INA226 ina_42;
WebServer update_server(8032); 
WebServer setting_server(80);
//初始化MQTT服务
WiFiClient esp_wifiClient;
PubSubClient mqtt_client(esp_wifiClient);

void setup() {
  Serial.begin(115200);
  //引脚初始化
  pinMode(ESP_PIN_BACKFLOW_MOS, OUTPUT);
  pinMode(ESP_PIN_PWM, OUTPUT);
  pinMode(ESP_PIN_PWM_EN, OUTPUT);
  pinMode(ESP_PIN_FAN, OUTPUT);
  pinMode(ESP_PIN_OUTPUT, OUTPUT);
  pinMode(ESP_PIN_INA_ALERT_1, INPUT_PULLUP);
  pinMode(ESP_PIN_INA_ALERT_2, INPUT_PULLUP);
  pinMode(ESP_PIN_INA_ALERT_3, INPUT_PULLUP);
  pinMode(ESP_PIN_EC11_A, INPUT);
  pinMode(ESP_PIN_EC11_B, INPUT);
  pinMode(ESP_PIN_EC11_K, INPUT);
  pinMode(ESP_PIN_SCREEN_BL, OUTPUT);
  digitalWrite(ESP_PIN_OUTPUT, HIGH);
  digitalWrite(ESP_PIN_SCREEN_BL, HIGH);
  //更新硬件参数
  Wire.setPins(ESP_PIN_IIC_SDA, ESP_PIN_IIC_SCL);        //I2C引脚设置
  sys_mac_address = WiFi.macAddress();                   //MAC地址更新
  sys_mac_address_pure =      sys_mac_address.substring(0,2) 
                              + sys_mac_address.substring(3,5) 
                              + sys_mac_address.substring(6,8) 
                              + sys_mac_address.substring(9,11) 
                              + sys_mac_address.substring(12,14) 
                              + sys_mac_address.substring(15,17);
  sys_mac_address_pure.toLowerCase();                        
  String mac_suffix = sys_mac_address_pure.substring(6,12);
  wifi_host                    = "mppt" + mac_suffix;                  //更新OTA HOST
  settings_ap_ssid             = "MPPT-" + mac_suffix;                 //AP名称
  settings_ap_psd              = "mppt" + mac_suffix;                  //AP连接密码
  Serial.println("HOST:" +wifi_host+ "  AP:" +settings_ap_ssid+ "  PD:" +settings_ap_psd);
  //初始化PWM波输出
  ledcSetup(system_pwm_channel, system_pwm_frequency, system_pwm_resolution);   //设置pwm参数
  ledcAttachPin(ESP_PIN_PWM, system_pwm_channel);                               //绑定输出引脚和通道
  ledcWrite(system_pwm_channel, system_pwm_value);                              //设定占空比，默认0
  system_pwm_max = pow(2, system_pwm_resolution) - 1;
  system_pwm_max_limit = (system_pwm_maxDC * system_pwm_max) / 100.000;         //计算最大占空比
  //读取设定参数
  LoadSettings();
  //读取系统历史数据
  load_sys_data();
  //初始化传感器
  sensors_init();
  //初始化buck
  buck_Disable();
  //加载显示屏
  SCREEN_init();
  //启用第二核心
  xTaskCreatePinnedToCore(coreTwo, "coreTwo", 10000, NULL, 0, &Core2, 0);
}

void loop() {
  SCREEN_loop();
  ec11.loop();
  outputController();
  readSensors();
  Device_Protection();
  Charging_Algorithm();
}

//================= 第二线程 =====================//
void coreTwo(void * pvParameters) {
  //初始化 setup
  //wifi初始化
  Serial.println(F("启动第二核心>>>"));
  Serial.print(wifi_ssid);
  Serial.print(F("    "));
  Serial.print(wifi_psd);
  Serial.print(F("    "));
  Serial.print(blinker_auth);
  Serial.print(F("    "));
  Serial.print(mqtt_server_add);
  Serial.print(F("    "));
  Serial.print(mqtt_server_port);
  Serial.print(F("    "));
  Serial.print(mqtt_usr);
  Serial.print(F("    "));
  Serial.print(mqtt_psd);
  Serial.print(F("    "));
  Serial.print(system_ota_usr);
  Serial.print(F("    "));
  Serial.println(system_ota_psd);
  //初始化网络连接
  internet_setup();
  //OTA初始化
  OTA_setup();
  Serial.println(F("第二核心初始化完成！"));
  MQTT_send_log(F("启动完成"));
  //循环 loop
  while (1) {
    networkController();
    daily_work_controller();           
    //定时保存运行历史数据
    if(millis()-system_storedata_lastmillis >= system_storedata_timeinterval){
      store_sys_data(); //保存数据到EEPROM
      system_storedata_lastmillis = millis();
      MQTT_send_log(F("自动保存数据"));
    }
  }
}

